package com.lagou.java;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
import org.apache.curator.retry.RetryNTimes;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import sun.security.krb5.Config;

import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * @Classname ZkBoostrap
 * @Description TODO
 * @Date 2020/8/20 12:59
 * @Created by kaifa
 */
@Slf4j
public class ZkBoostrap {

    public static final String JDBC_CONFIG_NODE = "JDBC_CONFIG_DETAILS";

    private static volatile   CuratorFramework client;

    private static volatile Lock lock = new ReentrantLock();

    public ZkBoostrap() {
        lock.lock();
        try {
            if (this.client == null) {
                this.client = CuratorFrameworkFactory.newClient(ZkPathEnum.SERVER_ADDR, new RetryNTimes(3, 1000));
                this.client.start();
                addListners();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    // 监控节点
    private void addListners() {
        try {
            PathChildrenCache childrenListner = new PathChildrenCache(client, ZkPathEnum.CONFIG, true);
            childrenListner.getListenable().addListener(new PathChildrenCacheListener() {
                @Override
                public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent event) throws Exception {
                    if (event != null && event.getData() != null) {
                        log.info("============================= netty 服务端节点信息发生变更 =============================================");
                        String path = event.getData().getPath();
                        if (path.startsWith(ZkPathEnum.CONFIG)) {
                            String childNode = path.substring(ZkPathEnum.CONFIG.length() + 1);
                            // 新增子节点
                            if (PathChildrenCacheEvent.Type.CHILD_ADDED.equals(event.getType())) {
                                String nodeVal = new String(event.getData().getData());
                                log.info("新增子节点, 节点为：{}, 详细信息为：{}", childNode, nodeVal);
                            }

                            // 子节点信息发生变更
                            if (PathChildrenCacheEvent.Type.CHILD_UPDATED.equals(event.getType())) {
                                ConfigInfo info = get();
                                DruidDataSource dataSource = (DruidDataSource) Application.context.getBean("dataSource");
                                //druid 数据库 init 后 无法重置初始化参数, 需要执行 restart 释放当前持有的连接信息
                                dataSource.restart();
                                // 此处仅 更新了连接信息
                                dataSource.setUrl(info.getUrl());
                                log.info("子节点信息发生变更, 节点为：{}, 详细信息为：{}", childNode, info);
                            }

                            // 子节点被删除
                            if (PathChildrenCacheEvent.Type.CHILD_REMOVED.equals(event.getType())) {
                                String nodedVal = new String(event.getData().getData());
                                log.warn("子节点被删除, 节点为：{}, 详细信息为：{}", childNode, nodedVal);
                            }
                            log.info("发生改变的的服务节点为：{}", event.getData().getData());
                        }
                    }

                }
            });
            // 开始监听
            childrenListner.start();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 创建节点
     * @param configInfo
     */
    public String create(ConfigInfo configInfo) {
        String node = null;
        try {
            node = getFullNodePath(JDBC_CONFIG_NODE);
            create(node, JSON.toJSON(configInfo));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return node;
    }

    public String create(String node, Object data) {
        try {
            Stat stat = client.checkExists().forPath(node);
            if (stat != null) {
                delete(node);
            }
            client.create()
                    .creatingParentsIfNeeded()
                    .withMode(CreateMode.EPHEMERAL)
                    .forPath(node, data.toString().getBytes());
            log.info("===>>> JDBC 配置节点 添加成功 ：{}，节点信信息详情：{}", node, data);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return node;
    }

    // 修改服务
    public String set(ConfigInfo configInfo) throws Exception {
        return set(getFullNodePath(JDBC_CONFIG_NODE), configInfo);
    }

    public String set(String node, Object data) {
        try {
            Stat stat = client.checkExists().forPath(node);
            if (stat != null) {
                byte[] bytes = client.getData().forPath(node);
                client.setData()
                        .forPath(node, JSON.toJSONString(data).getBytes());
                log.info("===>>> 服务端节点信息修改, 端点：{}，节点信信息详情：{}, 修改前信息为： {}", node, data, new String(bytes) );
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return node;
    }

    public ConfigInfo get () {
        return get(getFullNodePath(JDBC_CONFIG_NODE));
    }

    public ConfigInfo get(String node) {
        try {
            Stat stat = client.checkExists().forPath(node);
            if (stat != null) {
                byte[] bytes = client.getData().forPath(node);
                ConfigInfo info = JSON.parseObject(bytes, ConfigInfo.class);
                return info;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    public Map<String, ConfigInfo> getAllServerNodes()  throws Exception{
        Map<String, ConfigInfo> configInfoMap = new HashMap<>();
        List<String> nodeList = getClient().getChildren().forPath(ZkPathEnum.CONFIG);
        for (String node : nodeList) {
            byte[] bytes = getClient().getData().forPath(getFullNodePath(node));
            ConfigInfo configInfo = JSON.parseObject(bytes, ConfigInfo.class);
            configInfoMap.put(node, configInfo);
        }

        return configInfoMap;
    }

    // 关闭服务
    public void delete (String node) throws Exception{
        Stat stat = client.checkExists().forPath(node);
        if (stat != null) {
            byte[] bytes = client.getData().forPath(node);
            getClient().delete().deletingChildrenIfNeeded().forPath(node);
            log.info("服务节点：{}被删除， 详细信息为：{}", node, new String(bytes));
        } else {
            log.warn("服务节点：{} 不存在", node);
        }
    }

    public CuratorFramework getClient() {
        return client;
    }


    public String getFullNodePath(String node) {
        return ZkPathEnum.CONFIG + "/" + node;
    }

}
